<!DOCTYPE html>
<html lang=en>
<head>
    <!-- so meta -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="HandheldFriendly" content="True">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5" />
    <meta name="description" content="写在前面现在到处有微信支付的身影，作为一个后端开发者，跟我一起来看看微信支付到底怎么应用于自己的项目中吧 如果你还不了微信第三方服务生态的，请先阅读一下微信与阿里云第三方服务的一些概念流程梳理，相信读过后你会对微信第三方服务生态有了一定的了解，下面可以按照要求准备开通微信支付的必备条件，并开通APIv3证书，获取一些必备的参数。 如果你对密码学的常识不够了解，最好先阅读一下开发过程中那些不得不知道">
<meta property="og:type" content="article">
<meta property="og:title" content="跟我一起玩转微信APIv3支付">
<meta property="og:url" content="https://cheung0-bit.github.io/7f59debf3aa3/index.html">
<meta property="og:site_name" content="Bruce Zhang&#39;s Blogs">
<meta property="og:description" content="写在前面现在到处有微信支付的身影，作为一个后端开发者，跟我一起来看看微信支付到底怎么应用于自己的项目中吧 如果你还不了微信第三方服务生态的，请先阅读一下微信与阿里云第三方服务的一些概念流程梳理，相信读过后你会对微信第三方服务生态有了一定的了解，下面可以按照要求准备开通微信支付的必备条件，并开通APIv3证书，获取一些必备的参数。 如果你对密码学的常识不够了解，最好先阅读一下开发过程中那些不得不知道">
<meta property="og:locale" content="en_US">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/eed8a513f0b3f66479962dbb4261f018.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/d455ab9476ef43f5e63091b8dbd94156.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/785b2284194a0551fb01c9992039fff5.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/f113a030f2efeb7076e675f35edd33b1.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/d3936bb6f5db65be9b4d8c4a22aec153.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/41c40b6bc6eb00c939394b62546ad1f0.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/f219967214985b2d611008d6241df1cf.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/4a0c1dc88f3deb56b37278114e391b86.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/d978e3464d855d1e8b7fb2b867cce8dc.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/bd6cbfde08fef9a976b3fa6901b492ed.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/17787e29847c0f85dd4d60a62d79366e.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/efa0e9e2a55771ce481cd4c7496eee68.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/72be5fa336b6369a0f358d48970eff02.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/2e75dee75c01dcc499bb964c7e324284.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/d298a5e327d7ba8349053e6cc33f43b1.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/b6064f58ad650b46a39b03d8add91b1f.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/4aca03dd79f4e8a2f8ba415797685bfe.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/160c8c3a5e1440c79e8a2744f7ac9960.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/tsQQ%E5%9B%BE%E7%89%8720220311170526.jpg">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/fe570790b68cbd3c62facd35030c8120.png">
<meta property="og:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/1c7c9eb848adc654a680e62f5f964977.png">
<meta property="article:published_time" content="2022-03-11T07:02:34.597Z">
<meta property="article:modified_time" content="2022-11-25T07:02:58.342Z">
<meta property="article:author" content="张林">
<meta property="article:tag" content="微信SDK">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://0-bit.oss-cn-beijing.aliyuncs.com/eed8a513f0b3f66479962dbb4261f018.png">
    
    
      
        
          <link rel="shortcut icon" href="/images/favicon.ico">
        
      
      
        
          <link rel="icon" type="image/png" href="/images/favicon-192x192.png" sizes="192x192">
        
      
      
        
          <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon.png">
        
      
    
    <!-- title -->
    <title>跟我一起玩转微信APIv3支付</title>
    <!-- styles -->
    
<link rel="stylesheet" href="/css/style.css">

    <!-- persian styles -->
    
    <!-- rss -->
    
    
      <link rel="alternate" href="/atom.xml" title="Bruce Zhang&#39;s Blogs" type="application/atom+xml" />
    
	<!-- mathjax -->
	
<meta name="generator" content="Hexo 7.0.0"></head>

<body class="max-width mx-auto px3 ltr">
    
      <div id="header-post">
  <a id="menu-icon" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="menu-icon-tablet" href="#" aria-label="Menu"><i class="fas fa-bars fa-lg"></i></a>
  <a id="top-icon-tablet" href="#" aria-label="Top" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');" style="display:none;"><i class="fas fa-chevron-up fa-lg"></i></a>
  <span id="menu">
    <span id="nav">
      <ul>
        <!--
       --><li><a href="/">Home</a></li><!--
     --><!--
       --><li><a href="/about/">About</a></li><!--
     --><!--
       --><li><a href="/archives/">Articles</a></li><!--
     --><!--
       --><li><a href="/categories/">Category</a></li><!--
     --><!--
       --><li><a href="/search/">Search</a></li><!--
     -->
      </ul>
    </span>
    <br/>
    <span id="actions">
      <ul>
        
        <li><a class="icon" aria-label="Previous post" href="/f384b04dd874/"><i class="fas fa-chevron-left" aria-hidden="true" onmouseover="$('#i-prev').toggle();" onmouseout="$('#i-prev').toggle();"></i></a></li>
        
        
        <li><a class="icon" aria-label="Next post" href="/6f003f1be2ed/"><i class="fas fa-chevron-right" aria-hidden="true" onmouseover="$('#i-next').toggle();" onmouseout="$('#i-next').toggle();"></i></a></li>
        
        <li><a class="icon" aria-label="Back to top" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up" aria-hidden="true" onmouseover="$('#i-top').toggle();" onmouseout="$('#i-top').toggle();"></i></a></li>
        <li><a class="icon" aria-label="Share post" href="#"><i class="fas fa-share-alt" aria-hidden="true" onmouseover="$('#i-share').toggle();" onmouseout="$('#i-share').toggle();" onclick="$('#share').toggle();return false;"></i></a></li>
      </ul>
      <span id="i-prev" class="info" style="display:none;">Previous post</span>
      <span id="i-next" class="info" style="display:none;">Next post</span>
      <span id="i-top" class="info" style="display:none;">Back to top</span>
      <span id="i-share" class="info" style="display:none;">Share post</span>
    </span>
    <br/>
    <div id="share" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://cheung0-bit.github.io/7f59debf3aa3/"><i class="fab fa-facebook " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://cheung0-bit.github.io/7f59debf3aa3/&text=跟我一起玩转微信APIv3支付"><i class="fab fa-twitter " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-linkedin " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://cheung0-bit.github.io/7f59debf3aa3/&is_video=false&description=跟我一起玩转微信APIv3支付"><i class="fab fa-pinterest " aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=跟我一起玩转微信APIv3支付&body=Check out this article: https://cheung0-bit.github.io/7f59debf3aa3/"><i class="fas fa-envelope " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-get-pocket " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-reddit " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-stumbleupon " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-digg " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://cheung0-bit.github.io/7f59debf3aa3/&name=跟我一起玩转微信APIv3支付&description="><i class="fab fa-tumblr " aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://cheung0-bit.github.io/7f59debf3aa3/&t=跟我一起玩转微信APIv3支付"><i class="fab fa-hacker-news " aria-hidden="true"></i></a></li>
</ul>

    </div>
    <div id="toc">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%99%E5%9C%A8%E5%89%8D%E9%9D%A2"><span class="toc-number">1.</span> <span class="toc-text">写在前面</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%86%9F%E6%82%89%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3"><span class="toc-number">2.</span> <span class="toc-text">熟悉官方文档</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%9C%8B%E6%87%82SDK%E6%96%87%E6%A1%A3"><span class="toc-number">3.</span> <span class="toc-text">看懂SDK文档</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%81%9A%E5%A5%BD%E5%87%86%E5%A4%87"><span class="toc-number">3.1.</span> <span class="toc-text">做好准备</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%80%E5%A7%8B"><span class="toc-number">3.2.</span> <span class="toc-text">开始</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A1%AB%E5%9D%91"><span class="toc-number">3.3.</span> <span class="toc-text">填坑</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9E%E8%B0%83%E6%96%B9%E6%A1%88"><span class="toc-number">3.4.</span> <span class="toc-text">回调方案</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%80%E5%A7%8B%E5%B9%B2%E6%B4%BB"><span class="toc-number">4.</span> <span class="toc-text">开始干活</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%86%8D%E5%87%86%E5%A4%87%E4%B8%80%E6%AC%A1"><span class="toc-number">4.1.</span> <span class="toc-text">再准备一次</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B3%A8%E5%86%8C%E5%85%A8%E5%B1%80Bean-%E4%BE%9B%E4%B8%9A%E5%8A%A1%E4%BD%BF%E7%94%A8"><span class="toc-number">4.2.</span> <span class="toc-text">注册全局Bean 供业务使用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%B1%E4%BA%8E%E5%A4%9A%E5%A4%84%E9%9C%80%E8%A6%81%E5%8A%A0%E8%BD%BD%E7%A7%81%E9%92%A5%EF%BC%8C%E4%BE%BF%E6%B3%A8%E5%86%8C%E4%B8%80%E4%B8%AA%E8%BF%94%E5%9B%9E%E7%A7%81%E9%92%A5%E5%86%85%E5%AE%B9%E7%9A%84Bean"><span class="toc-number">4.2.1.</span> <span class="toc-text">由于多处需要加载私钥，便注册一个返回私钥内容的Bean</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E9%AA%8C%E7%AD%BE%E5%99%A8"><span class="toc-number">4.2.2.</span> <span class="toc-text">获取验签器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96HttpClient"><span class="toc-number">4.2.3.</span> <span class="toc-text">获取HttpClient</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-number">4.2.4.</span> <span class="toc-text">获取支付回调请求处理器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%81%9A%E6%B5%8B%E8%AF%95"><span class="toc-number">4.2.5.</span> <span class="toc-text">启动服务做测试</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9B%86%E4%B8%AD%E5%B0%81%E8%A3%85%E4%B8%80%E4%BA%9B%E6%9E%9A%E4%B8%BE%E7%B1%BB"><span class="toc-number">4.3.</span> <span class="toc-text">集中封装一些枚举类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%81%E8%A3%85%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF"><span class="toc-number">4.4.</span> <span class="toc-text">封装响应消息</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Native%E4%B8%8B%E5%8D%95"><span class="toc-number">4.5.</span> <span class="toc-text">Native下单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%96%E6%B6%88%E8%AE%A2%E5%8D%95"><span class="toc-number">4.6.</span> <span class="toc-text">取消订单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E8%AF%A2%E8%AE%A2%E5%8D%95"><span class="toc-number">4.7.</span> <span class="toc-text">查询订单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83"><span class="toc-number">4.8.</span> <span class="toc-text">支付回调</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">5.</span> <span class="toc-text">总结</span></a></li></ol>
    </div>
  </span>
</div>

    
    <div class="content index py4">
        
        <article class="post" itemscope itemtype="http://schema.org/BlogPosting">
  <header>
    
    <h1 class="posttitle" itemprop="name headline">
        跟我一起玩转微信APIv3支付
    </h1>



    <div class="meta">
      <span class="author" itemprop="author" itemscope itemtype="http://schema.org/Person">
        <span itemprop="name">张林</span>
      </span>
      
    <div class="postdate">
      
        <time datetime="2022-03-11T07:02:34.597Z" itemprop="datePublished">2022-03-11</time>
        
        (Updated: <time datetime="2022-11-25T07:02:58.342Z" itemprop="dateModified">2022-11-25</time>)
        
      
    </div>


      
    <div class="article-category">
        <i class="fas fa-archive"></i>
        <a class="category-link" href="/categories/%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/">开发实践</a>
    </div>


      
    <div class="article-tag">
        <i class="fas fa-tag"></i>
        <a class="tag-link-link" href="/tags/%E5%BE%AE%E4%BF%A1SDK/" rel="tag">微信SDK</a>
    </div>


    </div>
  </header>
  

  <div class="content" itemprop="articleBody">
    <h2 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h2><p>现在到处有微信支付的身影，作为一个后端开发者，跟我一起来看看微信支付到底怎么应用于自己的项目中吧</p>
<p>如果你还不了微信第三方服务生态的，请先阅读一下<a target="_blank" rel="noopener" href="https://blog.csdn.net/m0_52038897/article/details/123101493?spm=1001.2014.3001.5501">微信与阿里云第三方服务的一些概念流程梳理</a>，相信读过后你会对微信第三方服务生态有了一定的了解，下面可以按照要求准备开通微信支付的必备条件，并开通APIv3证书，获取一些必备的参数。</p>
<p>如果你对密码学的常识不够了解，最好先阅读一下<a target="_blank" rel="noopener" href="https://blog.csdn.net/m0_52038897/article/details/123319493?spm=1001.2014.3001.5501">开发过程中那些不得不知道的密码学基础</a>。基于这些常识，理解这篇文章将会事半功倍。</p>
<h2 id="熟悉官方文档"><a href="#熟悉官方文档" class="headerlink" title="熟悉官方文档"></a>熟悉官方文档</h2><p>如果你在之前已经有尝试过浏览微信支付的官方文档&#x2F;SDK，你或许会和我一样一头雾水。因此，我会带着大家熟悉一下文档。</p>
<p>以Native支付为例，先是<a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml#part-6">开发指引</a>。在这里，微信支付高屋建瓴的总结了实现微信支付的流程。</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/eed8a513f0b3f66479962dbb4261f018.png" alt="image-20220311134633330"></p>
<p>首先说明了微信支付接口基于APIv3（贴张官方图说明一下）</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/d455ab9476ef43f5e63091b8dbd94156.png" alt="image-20220311135145870"></p>
<p>一言以蔽之，APIv3采用JSON作为数据交互格式，接口遵循REST风格，使用高效安全的SHA256-RSA签名算法保证数据完整性和请求者身份。不需要HTTPS客户端证书，仅凭借证书序列号换取公钥和签名内容，使用AES-256-GCM对称加密保证数据私密性。</p>
<p>作为开发者，两个东西需要保管好，极为重要，不可泄漏</p>
<ul>
<li>商户API证书</li>
</ul>
<p>商户证书中封装了公钥和签名内容，用于发送请求和签名验证</p>
<ul>
<li>商户API私钥</li>
</ul>
<p>商户申请商户API证书时，会生成商户私钥，并保存在本地证书文件夹的文件<code>apiclient_key.pem</code> 中。私钥用于获取AES加密口令，并解密获取加密内容</p>
<p>其次，在开发准备中提供了JAVA,PHP,GO三个语言版本的SDK，封装了签名生成、签名验证、敏感信息加解密、媒体文件上传等功能，方便我们直接使用，而不用自己手写这一系列的操作。若您对官方SDK不放心，可以自己实现。实现思路官方也给出了：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/785b2284194a0551fb01c9992039fff5.png" alt="image-20220311141242738"></p>
<p>也有相应的快速测试方法：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/f113a030f2efeb7076e675f35edd33b1.png" alt="image-20220311141328879"></p>
<p>然后，在快速接入中提供了业务流程图和相应功能（下单、查单、关单、回调支付）的实现逻辑</p>
<p>忽略官方示意图的一些细节，我画了一个更易于理解流程的时序图：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/d3936bb6f5db65be9b4d8c4a22aec153.png" alt="image-20220311143031084"></p>
<p>下面，我们要做的是就一目了然了：了解怎么使用SDK封装一系列的安全保障过程，即构建自动化的HttpClient，然后具体实现每个API的请求就可以了！</p>
<h2 id="看懂SDK文档"><a href="#看懂SDK文档" class="headerlink" title="看懂SDK文档"></a>看懂SDK文档</h2><h3 id="做好准备"><a href="#做好准备" class="headerlink" title="做好准备"></a>做好准备</h3><p>这里以Java为例，剖析官方SDK的README文档</p>
<p><a target="_blank" rel="noopener" href="https://github.com/wechatpay-apiv3/wechatpay-apache-httpclient">文档地址</a> 在这里建议大家下载源码到本地，更方便的阅读源码，对实现细节做了解</p>
<p>我写这篇文章的时间是</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">mysql&gt; select now();</span><br><span class="line">+---------------------+</span><br><span class="line">| now()               |</span><br><span class="line">+---------------------+</span><br><span class="line">| 2022-03-11 14:39:46 |</span><br><span class="line">+---------------------+</span><br><span class="line">1 row in set (0.03 sec)</span><br></pre></td></tr></table></figure>

<p>采用最新版本SDK <code>wechatpay-apache-httpclient</code> <code>0.4.2</code></p>
<p>基于JDK1.8+ Maven依赖为</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.github.wechatpay-apiv3<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>wechatpay-apache-httpclient<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>0.4.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/41c40b6bc6eb00c939394b62546ad1f0.png" alt="image-20220311144210416"></p>
<p>这里告诉我们凭借商户号、证书序列号、私钥、商户证书等可构建专有的WechatPayHttpClient，帮助我们实现加解密、签名、签名验证等繁琐的过程</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/f219967214985b2d611008d6241df1cf.png" alt="image-20220311144428678"></p>
<p>接下来则是使用该HttpClient如果封装请求头和请求体的简单实例</p>
<h3 id="填坑"><a href="#填坑" class="headerlink" title="填坑"></a>填坑</h3><p>通过上面的方式，需要手动下载更新证书。在README的后面给予了解决方法</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/4a0c1dc88f3deb56b37278114e391b86.png" alt="image-20220311144859088"></p>
<p>不同于上面，这里利用<code>CertificatesManager</code>证书管理器实现验签器、证书更新的集中管理</p>
<h3 id="回调方案"><a href="#回调方案" class="headerlink" title="回调方案"></a>回调方案</h3><p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/d978e3464d855d1e8b7fb2b867cce8dc.png" alt="image-20220311145056863"></p>
<p>这里则提供了如何使用SDK进行回调签名验证，返回数据的解密。稍微分析一下，可见SDK提供了NotificationRequest和NotificationHandler两个工具实现此功能。</p>
<p><strong>现在感到懵逼不要紧张，下面结合具体实例来说明</strong></p>
<h2 id="开始干活"><a href="#开始干活" class="headerlink" title="开始干活"></a>开始干活</h2><h3 id="再准备一次"><a href="#再准备一次" class="headerlink" title="再准备一次"></a>再准备一次</h3><p>相信看过上面对于微信文档和SDK文档的大致分析后，对大概怎么个流程已经心里有数了。下面就开始干活。</p>
<p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml">开发指引</a>提供了通用的解决思路（如何加载商户私钥、加载平台证书、初始化httpClient），只可惜其中<code>AutoUpdateCertificatesVerifier</code>在最新的SDK中已经弃用</p>
<p><strong><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/bd6cbfde08fef9a976b3fa6901b492ed.png" alt="image-20220311145744644"></strong></p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/17787e29847c0f85dd4d60a62d79366e.png" alt="image-20220311145943104"></p>
<p>虽然但是，开发者提供了更好的解决方法：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/efa0e9e2a55771ce481cd4c7496eee68.png" alt="image-20220311150055608"></p>
<p>此工具集成了获取证书，下载证书，定期更新证书，获取验签器功能为一体</p>
<p>看看该类的结构：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/72be5fa336b6369a0f358d48970eff02.png" alt="image-20220311150240045"></p>
<p>显然，这是单例模式的设计。getInstance方法获得唯一实例，可以放入证书，停止下载更新，获取验签器。</p>
<h3 id="注册全局Bean-供业务使用"><a href="#注册全局Bean-供业务使用" class="headerlink" title="注册全局Bean 供业务使用"></a>注册全局Bean 供业务使用</h3><p>经过上面的分析，不难得出。拿到CertificatesManager实例，放入证书，开启自动下载更新，取出验签器即可。并在SpringBoot服务中注册全局Bean，静候差遣！</p>
<p>当然，在这之前，最好在yml中配置好需要用到的参数，并读取到<code>WxPayConfig</code>中：</p>
<figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">wxpay:</span></span><br><span class="line">  <span class="comment"># 商户号</span></span><br><span class="line">  <span class="attr">mch-id:</span> <span class="number">1</span><span class="string">******42</span></span><br><span class="line">  <span class="comment"># API证书序列号</span></span><br><span class="line">  <span class="attr">mch-serial-no:</span> <span class="string">你的API证书序列号</span></span><br><span class="line">  <span class="comment"># 商户私钥文件</span></span><br><span class="line">  <span class="attr">private-key-path:</span> <span class="string">C:\Users\cheung0\Desktop\apiclient_key.pem</span></span><br><span class="line">  <span class="comment"># APIv3 密钥</span></span><br><span class="line">  <span class="attr">api-v3-key:</span> <span class="string">w*************x</span></span><br><span class="line">  <span class="comment"># APPID</span></span><br><span class="line">  <span class="attr">appid:</span> <span class="string">wx3*********46</span></span><br><span class="line">  <span class="comment"># 微信服务器地址</span></span><br><span class="line">  <span class="attr">domain:</span> <span class="string">https://api.mch.weixin.qq.com</span></span><br><span class="line">  <span class="comment"># 接受结果通知地址</span></span><br><span class="line">  <span class="attr">notify-domain:</span> <span class="string">http://maiqu.sh1.k9s.run:2271</span>   </span><br></pre></td></tr></table></figure>

<p>其中<code>notify-domain</code>是回调通知时为微信服务器请求的地址。若要做本地测试，请用内网穿透工具开通隧道。这里推荐我使用的<a target="_blank" rel="noopener" href="https://gitee.com/Hgui/FastTunnel.SuiDao">SuiDao</a></p>
<p>随后配置到</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@ConfigurationProperties(prefix = &quot;wxpay&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WxPayConfig</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 商户号</span></span><br><span class="line">    <span class="keyword">private</span> String mchId;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// API证书序列号</span></span><br><span class="line">    <span class="keyword">private</span> String mchSerialNo;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 私钥地址</span></span><br><span class="line">    <span class="keyword">private</span> String privateKeyPath;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// APIv3 密钥</span></span><br><span class="line">    <span class="keyword">private</span> String apiV3Key;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// APPID</span></span><br><span class="line">    <span class="keyword">private</span> String appid;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 微信服务器地址</span></span><br><span class="line">    <span class="keyword">private</span> String domain;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 接收结果通知地址</span></span><br><span class="line">    <span class="keyword">private</span> String notifyDomain;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这里推荐添加POM依赖，对配置和实体之间更好的依赖:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">&lt;!--配置映射--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.springframework.boot<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>spring-boot-configuration-processor<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">optional</span>&gt;</span>true<span class="tag">&lt;/<span class="name">optional</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>下面，可以自己写一个测试看看有没有配置上</p>
<p>接下来开始注册Bean</p>
<h4 id="由于多处需要加载私钥，便注册一个返回私钥内容的Bean"><a href="#由于多处需要加载私钥，便注册一个返回私钥内容的Bean" class="headerlink" title="由于多处需要加载私钥，便注册一个返回私钥内容的Bean"></a>由于多处需要加载私钥，便注册一个返回私钥内容的Bean</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 加载商户私钥 &lt;br&gt;</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> PrivateKey</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> PrivateKey <span class="title function_">getPrivateKey</span><span class="params">()</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 加载商户私钥（privateKey：私钥字符串）</span></span><br><span class="line">    log.info(<span class="string">&quot;开始加载私钥，读取内容...&quot;</span>);</span><br><span class="line">    <span class="type">String</span> <span class="variable">content</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">String</span>(Files.readAllBytes(Paths.get(privateKeyPath)),StandardCharsets.UTF_8 );</span><br><span class="line">    <span class="keyword">return</span> PemUtil.loadPrivateKey(<span class="keyword">new</span> <span class="title class_">ByteArrayInputStream</span>(content.getBytes(StandardCharsets.UTF_8)));</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>PemUtil是SDK提供的工具类，它可以帮助我们读取私钥：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> PrivateKey <span class="title function_">loadPrivateKey</span><span class="params">(String privateKey)</span> &#123;</span><br><span class="line">        privateKey = privateKey.replace(<span class="string">&quot;-----BEGIN PRIVATE KEY-----&quot;</span>, <span class="string">&quot;&quot;</span>).replace(<span class="string">&quot;-----END PRIVATE KEY-----&quot;</span>, <span class="string">&quot;&quot;</span>).replaceAll(<span class="string">&quot;\\s+&quot;</span>, <span class="string">&quot;&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">KeyFactory</span> <span class="variable">kf</span> <span class="operator">=</span> KeyFactory.getInstance(<span class="string">&quot;RSA&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> kf.generatePrivate(<span class="keyword">new</span> <span class="title class_">PKCS8EncodedKeySpec</span>(Base64.getDecoder().decode(privateKey)));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchAlgorithmException var2) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;当前Java环境不支持RSA&quot;</span>, var2);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InvalidKeySpecException var3) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;无效的密钥格式&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<h4 id="获取验签器"><a href="#获取验签器" class="headerlink" title="获取验签器"></a>获取验签器</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取平台证书管理器，定时更新证书（默认值为UPDATE_INTERVAL_MINUTE）</span></span><br><span class="line"><span class="comment"> * &lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 返回验签器实例，注册为bean，在实际业务中使用</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> Verifier <span class="title function_">getVerifier</span><span class="params">(PrivateKey merchantPrivateKey)</span> <span class="keyword">throws</span> IOException, NotFoundException &#123;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;加载证书管理器实例&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取证书管理器单例实例</span></span><br><span class="line">    <span class="type">CertificatesManager</span> <span class="variable">certificatesManager</span> <span class="operator">=</span> CertificatesManager.getInstance();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 向证书管理器增加需要自动更新平台证书的商户信息</span></span><br><span class="line">    log.info(<span class="string">&quot;向证书管理器增加商户信息，并开启自动更新&quot;</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 该方法底层已实现同步线程更新证书</span></span><br><span class="line">        <span class="comment">// 详见beginScheduleUpdate()方法</span></span><br><span class="line">        certificatesManager.putMerchant(mchId, <span class="keyword">new</span> <span class="title class_">WechatPay2Credentials</span>(mchId,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">PrivateKeySigner</span>(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (GeneralSecurityException | HttpCodeException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;从证书管理器中获取验签器&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> certificatesManager.getVerifier(mchId);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>至此，获取验签器的同时也开启了定时下载更新证书，在<code>certificatesManager.putMerchant</code>方法中可见：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">putMerchant</span><span class="params">(String merchantId, Credentials credentials, <span class="type">byte</span>[] apiV3Key)</span> <span class="keyword">throws</span> IOException, GeneralSecurityException, HttpCodeException &#123;</span><br><span class="line">        <span class="keyword">if</span> (merchantId != <span class="literal">null</span> &amp;&amp; !merchantId.isEmpty()) &#123;</span><br><span class="line">            <span class="keyword">if</span> (credentials == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;credentials为空&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (apiV3Key.length == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;apiV3Key为空&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.certificates.get(merchantId) == <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.certificates.put(merchantId, <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>());</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="built_in">this</span>.initCertificates(merchantId, credentials, apiV3Key);</span><br><span class="line">                <span class="built_in">this</span>.credentialsMap.put(merchantId, credentials);</span><br><span class="line">                <span class="built_in">this</span>.apiV3Keys.put(merchantId, apiV3Key);</span><br><span class="line">                <span class="keyword">if</span> (<span class="built_in">this</span>.executor == <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="built_in">this</span>.beginScheduleUpdate();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;merchantId为空&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">beginScheduleUpdate</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.executor = <span class="keyword">new</span> <span class="title class_">SafeSingleScheduleExecutor</span>();</span><br><span class="line">        <span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.currentThread().setName(<span class="string">&quot;scheduled_update_cert_thread&quot;</span>);</span><br><span class="line">                log.info(<span class="string">&quot;Begin update Certificates.Date:&#123;&#125;&quot;</span>, Instant.now());</span><br><span class="line">                <span class="built_in">this</span>.updateCertificates();</span><br><span class="line">                log.info(<span class="string">&quot;Finish update Certificates.Date:&#123;&#125;&quot;</span>, Instant.now());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable var2) &#123;</span><br><span class="line">                log.error(<span class="string">&quot;Update Certificates failed&quot;</span>, var2);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;;</span><br><span class="line">        <span class="built_in">this</span>.executor.scheduleAtFixedRate(runnable, <span class="number">0L</span>, <span class="number">1440L</span>, TimeUnit.MINUTES);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<p>不难看出，当SpringBoot服务启动后，线程池中会创建一个名为”scheduled_update_cert_thread”的线程来定时下载更新证书</p>
<h4 id="获取HttpClient"><a href="#获取HttpClient" class="headerlink" title="获取HttpClient"></a>获取HttpClient</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 通过WechatPayHttpClientBuilder构造HttpClient</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> verifier</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line"><span class="meta">@Bean(name = &quot;wxPayClient&quot;)</span></span><br><span class="line"><span class="keyword">public</span> CloseableHttpClient <span class="title function_">getWxPayClient</span><span class="params">(Verifier verifier,PrivateKey merchantPrivateKey)</span> <span class="keyword">throws</span> IOException &#123;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;构造httpClient&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="type">WechatPayHttpClientBuilder</span> <span class="variable">builder</span> <span class="operator">=</span> WechatPayHttpClientBuilder.create()</span><br><span class="line">        .withMerchant(mchId, mchSerialNo, merchantPrivateKey)</span><br><span class="line">        .withValidator(<span class="keyword">new</span> <span class="title class_">WechatPay2Validator</span>(verifier));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新</span></span><br><span class="line">    <span class="type">CloseableHttpClient</span> <span class="variable">httpClient</span> <span class="operator">=</span> builder.build();</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;构造httpClient成功&quot;</span>);</span><br><span class="line">    <span class="keyword">return</span> httpClient;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="获取支付回调请求处理器"><a href="#获取支付回调请求处理器" class="headerlink" title="获取支付回调请求处理器"></a>获取支付回调请求处理器</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">  * 构建微信支付回调请求处理器</span></span><br><span class="line"><span class="comment">  *</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@param</span> verifier</span></span><br><span class="line"><span class="comment">  * <span class="doctag">@return</span> NotificationHandler</span></span><br><span class="line"><span class="comment">  */</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="keyword">public</span> NotificationHandler <span class="title function_">notificationHandler</span><span class="params">(Verifier verifier)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">NotificationHandler</span>(verifier,apiV3Key.getBytes(StandardCharsets.UTF_8));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="启动服务做测试"><a href="#启动服务做测试" class="headerlink" title="启动服务做测试"></a>启动服务做测试</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">2022-03-11 15:54:27.683  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 开始加载私钥，读取内容...</span><br><span class="line">2022-03-11 15:54:27.697  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 加载证书管理器实例</span><br><span class="line">2022-03-11 15:54:27.698  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 向证书管理器增加商户信息，并开启自动更新</span><br><span class="line">2022-03-11 15:54:28.363  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 从证书管理器中获取验签器</span><br><span class="line">2022-03-11 15:54:28.365  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 构造httpClient</span><br><span class="line">2022-03-11 15:54:28.364  INFO 4944 --- [ate_cert_thread] c.w.p.c.a.h.cert.CertificatesManager     : Begin update Certificates.Date:2022-03-11T07:54:28.364Z</span><br><span class="line">2022-03-11 15:54:28.367  INFO 4944 --- [  restartedMain] t.m.metrichall.wxpay.config.WxPayConfig  : 构造httpClient成功</span><br><span class="line">2022-03-11 15:54:28.626  INFO 4944 --- [ate_cert_thread] c.w.p.c.a.h.cert.CertificatesManager     : Finish update Certificates.Date:2022-03-11T07:54:28.626Z</span><br></pre></td></tr></table></figure>

<p><strong>可以看到，我们注册Bean实例已启动，下载更新证书线程也启动了，一切准备完毕，静候差遣</strong></p>
<h3 id="集中封装一些枚举类"><a href="#集中封装一些枚举类" class="headerlink" title="集中封装一些枚举类"></a>集中封装一些枚举类</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">PayType</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 微信</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    WXPAY(<span class="string">&quot;微信&quot;</span>),</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 支付宝</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ALIPAY(<span class="string">&quot;支付宝&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">OrderStatus</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 未支付</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    NOTPAY(<span class="string">&quot;未支付&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 支付成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    SUCCESS(<span class="string">&quot;支付成功&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 已关闭</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CLOSED(<span class="string">&quot;超时已关闭&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 已取消</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CANCEL(<span class="string">&quot;用户已取消&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款中</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REFUND_PROCESSING(<span class="string">&quot;退款中&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 已退款</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REFUND_SUCCESS(<span class="string">&quot;已退款&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REFUND_ABNORMAL(<span class="string">&quot;退款异常&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">WxApiType</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Native下单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    NATIVE_PAY(<span class="string">&quot;/v3/pay/transactions/native&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询订单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ORDER_QUERY_BY_NO(<span class="string">&quot;/v3/pay/transactions/out-trade-no/%s&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 关闭订单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CLOSE_ORDER_BY_NO(<span class="string">&quot;/v3/pay/transactions/out-trade-no/%s/close&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 申请退款</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    DOMESTIC_REFUNDS(<span class="string">&quot;/v3/refund/domestic/refunds&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 查询单笔退款</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    DOMESTIC_REFUNDS_QUERY(<span class="string">&quot;/v3/refund/domestic/refunds/%s&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 申请交易账单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    TRADE_BILLS(<span class="string">&quot;/v3/bill/tradebill&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 申请资金账单</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    FUND_FLOW_BILLS(<span class="string">&quot;/v3/bill/fundflowbill&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">WxNotifyType</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 支付通知</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    NATIVE_NOTIFY(<span class="string">&quot;/api/wx-pay/native/notify&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款结果通知</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REFUND_NOTIFY(<span class="string">&quot;/api/wx-pay/refunds/notify&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">WxRefundStatus</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    SUCCESS(<span class="string">&quot;SUCCESS&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款关闭</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CLOSED(<span class="string">&quot;CLOSED&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款处理中</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    PROCESSING(<span class="string">&quot;PROCESSING&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 退款异常</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    ABNORMAL(<span class="string">&quot;ABNORMAL&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@AllArgsConstructor</span></span><br><span class="line"><span class="meta">@Getter</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">WxTradeState</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 支付成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    SUCCESS(<span class="string">&quot;SUCCESS&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 未支付</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    NOTPAY(<span class="string">&quot;NOTPAY&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 已关闭</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    CLOSED(<span class="string">&quot;CLOSED&quot;</span>),</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 转入退款</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    REFUND(<span class="string">&quot;REFUND&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 类型</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String type;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>枚举类中的值在业务经常会被用到，封装成枚举类，更为优雅</p>
<h3 id="封装响应消息"><a href="#封装响应消息" class="headerlink" title="封装响应消息"></a>封装响应消息</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="meta">@Accessors(chain = true)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">R</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> Integer code; <span class="comment">//响应码</span></span><br><span class="line">    <span class="keyword">private</span> String message; <span class="comment">//响应消息</span></span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Object&gt; data = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> R <span class="title function_">ok</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">R</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">R</span>();</span><br><span class="line">        r.setCode(<span class="number">0</span>);</span><br><span class="line">        r.setMessage(<span class="string">&quot;成功&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> r;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> R <span class="title function_">error</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="type">R</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">R</span>();</span><br><span class="line">        r.setCode(-<span class="number">1</span>);</span><br><span class="line">        r.setMessage(<span class="string">&quot;失败&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> r;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> R <span class="title function_">data</span><span class="params">(String key, Object value)</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.data.put(key, value);</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>其中<code>@Accessors(chain = true)</code>注解可使得该类方法可以链式调用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> R.ok().setMessage(<span class="string">&quot;下单成功!&quot;</span>)</span><br></pre></td></tr></table></figure>

<h3 id="Native下单"><a href="#Native下单" class="headerlink" title="Native下单"></a>Native下单</h3><p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml">Native下单API字典</a>告知了我们必要的参数，并提供了请求示例：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">	<span class="attr">&quot;mchid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1900006XXX&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;out_trade_no&quot;</span><span class="punctuation">:</span> <span class="string">&quot;native12177525012014070332333&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;appid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;wxdace645e0bc2cXXX&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;description&quot;</span><span class="punctuation">:</span> <span class="string">&quot;Image形象店-深圳腾大-QQ公仔&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;notify_url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;https://weixin.qq.com/&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;amount&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">		<span class="attr">&quot;total&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">		<span class="attr">&quot;currency&quot;</span><span class="punctuation">:</span> <span class="string">&quot;CNY&quot;</span></span><br><span class="line">	<span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>返回示例：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">	<span class="attr">&quot;code_url&quot;</span><span class="punctuation">:</span> <span class="string">&quot;weixin://wxpay/bizpayurl?pr=p4lpSuKzz&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>如果成功，就能拿到二维码链接</p>
<p>按照示例，封装我们自己的请求体：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建订单，调用Native支付接口</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> productId</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> code_url 和 订单号</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Transactional(rollbackFor = Exception.class)</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> Map&lt;String, Object&gt; <span class="title function_">nativePay</span><span class="params">(Long productId)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;生成订单&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//生成订单...</span></span><br><span class="line">    </span><br><span class="line">    </span><br><span class="line">    <span class="comment">//查找二维码链接是否已经存在 ? 直接retun : 往下走 ...</span></span><br><span class="line">   </span><br><span class="line"></span><br><span class="line">		</span><br><span class="line">    log.info(<span class="string">&quot;调用统一下单API&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//调用统一下单API</span></span><br><span class="line">    <span class="type">HttpPost</span> <span class="variable">httpPost</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HttpPost</span>(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY.getType()));</span><br><span class="line">    httpPost.addHeader(<span class="string">&quot;Accept&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">    httpPost.addHeader(<span class="string">&quot;Content-type&quot;</span>, <span class="string">&quot;application/json; charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 请求body参数</span></span><br><span class="line">    Map&lt;String, Object&gt; paramsMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    paramsMap.put(<span class="string">&quot;mchid&quot;</span>, wxPayConfig.getMchId());</span><br><span class="line">    paramsMap.put(<span class="string">&quot;out_trade_no&quot;</span>, orderInfo.getOrderNo());</span><br><span class="line">    paramsMap.put(<span class="string">&quot;appid&quot;</span>, wxPayConfig.getAppid());</span><br><span class="line">    paramsMap.put(<span class="string">&quot;description&quot;</span>, orderInfo.getTitle());</span><br><span class="line">    paramsMap.put(<span class="string">&quot;notify_url&quot;</span>, wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY.getType()));</span><br><span class="line">    <span class="comment">// 组装amount</span></span><br><span class="line">    Map&lt;String, Object&gt; amountMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    amountMap.put(<span class="string">&quot;total&quot;</span>, orderInfo.getTotalFee());</span><br><span class="line">    amountMap.put(<span class="string">&quot;currency&quot;</span>, <span class="string">&quot;CNY&quot;</span>);</span><br><span class="line">    paramsMap.put(<span class="string">&quot;amount&quot;</span>, amountMap);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将参数转换成json字符串</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">jsonParams</span> <span class="operator">=</span> JSON.toJSONString(paramsMap);</span><br><span class="line">    log.info(<span class="string">&quot;请求参数 ===&gt; &#123;&#125;&quot;</span> + jsonParams);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 配置请求体</span></span><br><span class="line">    httpPost.setEntity(<span class="keyword">new</span> <span class="title class_">StringEntity</span>(jsonParams, <span class="string">&quot;UTF-8&quot;</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">//完成签名并执行请求</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> (<span class="type">CloseableHttpResponse</span> <span class="variable">response</span> <span class="operator">=</span> httpClient.execute(httpPost)) &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">bodyAsString</span> <span class="operator">=</span> EntityUtils.toString(response.getEntity());<span class="comment">//响应体</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">statusCode</span> <span class="operator">=</span> response.getStatusLine().getStatusCode();<span class="comment">//响应状态码</span></span><br><span class="line">        <span class="keyword">if</span> (statusCode == <span class="number">200</span>) &#123; <span class="comment">//处理成功</span></span><br><span class="line">            log.info(<span class="string">&quot;成功, 返回结果 = &quot;</span> + bodyAsString);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (statusCode == <span class="number">204</span>) &#123; <span class="comment">//处理成功，无返回Body</span></span><br><span class="line">            log.info(<span class="string">&quot;成功&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;Native下单失败,响应码 = &quot;</span> + statusCode + <span class="string">&quot;,返回结果 = &quot;</span> + bodyAsString);</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(<span class="string">&quot;request failed&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//响应结果</span></span><br><span class="line">        Map&lt;String, String&gt; resultMap = JSON.parseObject(bodyAsString, HashMap.class);</span><br><span class="line">        <span class="comment">//二维码</span></span><br><span class="line">        codeUrl = resultMap.get(<span class="string">&quot;code_url&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//保存二维码链接...</span></span><br><span class="line">       </span><br><span class="line"></span><br><span class="line">        <span class="comment">//返回二维码</span></span><br><span class="line">        Map&lt;String, Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">        map.put(<span class="string">&quot;codeUrl&quot;</span>, codeUrl);</span><br><span class="line">        map.put(<span class="string">&quot;orderNo&quot;</span>, orderInfo.getOrderNo());</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> map;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>其中省略部分则大家个性化开发，使用Mybatis&#x2F;MybatisPlus&#x2F;JPA等工具进行数据库的增删改查，接下来类似地方同理</p>
</blockquote>
<p>API请求看一下：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/2e75dee75c01dcc499bb964c7e324284.png" alt="image-20220311160006916"></p>
<p>返回JSON：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;成功&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;codeUrl&quot;</span><span class="punctuation">:</span> <span class="string">&quot;weixin://wxpay/bizpayurl?pr=HVPisQfzz&quot;</span><span class="punctuation">,</span></span><br><span class="line">    <span class="attr">&quot;orderNo&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ORDER_20220311155916957&quot;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>codeUrl就是我们二维码的链接，后端可以采用Zxing工具来解析成二维码图片二进制流返回前端。我这里交给前端同学自行做优化处理。</p>
<p>在这里使用QRcode测试一下该codeUrl</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;UTF-8&quot;</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">title</span>&gt;</span>支付测试<span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&quot;https://code.jquery.com/jquery-3.3.1.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">script</span> <span class="attr">type</span>=<span class="string">&quot;text/javascript&quot;</span> <span class="attr">src</span>=<span class="string">&quot;https://cdn.bootcss.com/jquery.qrcode/1.0/jquery.qrcode.min.js&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">button</span> <span class="attr">onclick</span>=<span class="string">&quot;displayDate()&quot;</span>&gt;</span>点击支付<span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">&quot;myQrcode&quot;</span>&gt;</span><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">	<span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript">		<span class="keyword">function</span> <span class="title function_">displayDate</span>(<span class="params"></span>) &#123;</span></span><br><span class="line"><span class="language-javascript">			<span class="title function_">jQuery</span>(<span class="string">&#x27;#myQrcode&#x27;</span>).<span class="title function_">qrcode</span>(&#123;</span></span><br><span class="line"><span class="language-javascript">				<span class="attr">text</span>: <span class="string">&#x27;weixin://wxpay/bizpayurl?pr=HVPisQfzz&#x27;</span></span></span><br><span class="line"><span class="language-javascript">			&#125;);</span></span><br><span class="line"><span class="language-javascript">		&#125;</span></span><br><span class="line"><span class="language-javascript">	</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h3 id="取消订单"><a href="#取消订单" class="headerlink" title="取消订单"></a>取消订单</h3><p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_3.shtml">取消订单API字典</a>告诉了我们需要的参数：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/d298a5e327d7ba8349053e6cc33f43b1.png" alt="image-20220311160909354"></p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">	<span class="attr">&quot;mchid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1230000109&quot;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p>请求体中放商户号，订单号拼接在URL中即可</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 关单接口的调用</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * API字典： https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_3.shtml</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> orderNo</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> HashMap&lt;String, String&gt; <span class="title function_">closeOrder</span><span class="params">(String orderNo)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;关单接口的调用，订单号 ===&gt; &#123;&#125;&quot;</span>, orderNo);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//创建远程请求对象</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> String.format(WxApiType.CLOSE_ORDER_BY_NO.getType(), orderNo);</span><br><span class="line">    url = wxPayConfig.getDomain().concat(url);</span><br><span class="line">    <span class="type">HttpPost</span> <span class="variable">httpPost</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HttpPost</span>(url);</span><br><span class="line">    httpPost.addHeader(<span class="string">&quot;Accept&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line">    httpPost.addHeader(<span class="string">&quot;Content-type&quot;</span>, <span class="string">&quot;application/json; charset=utf-8&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 请求body参数</span></span><br><span class="line">    Map&lt;String, String&gt; paramsMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    paramsMap.put(<span class="string">&quot;mchid&quot;</span>, wxPayConfig.getMchId());</span><br><span class="line">    <span class="type">String</span> <span class="variable">jsonParams</span> <span class="operator">=</span> JSON.toJSONString(paramsMap);</span><br><span class="line">    log.info(<span class="string">&quot;请求参数 ===&gt; &#123;&#125;&quot;</span>, jsonParams);</span><br><span class="line"></span><br><span class="line">    <span class="type">StringEntity</span> <span class="variable">entity</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringEntity</span>(jsonParams, <span class="string">&quot;UTF-8&quot;</span>);</span><br><span class="line">    entity.setContentType(<span class="string">&quot;application/json&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//将请求参数设置到请求对象中</span></span><br><span class="line">    httpPost.setEntity(entity);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//完成签名并执行请求</span></span><br><span class="line">    <span class="type">CloseableHttpResponse</span> <span class="variable">response</span> <span class="operator">=</span> httpClient.execute(httpPost);</span><br><span class="line"></span><br><span class="line">    HashMap&lt;String, String&gt; res = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (response.getStatusLine().getStatusCode() == <span class="number">200</span> || response.getStatusLine().getStatusCode() == <span class="number">204</span> ) &#123;</span><br><span class="line">            res.put(<span class="string">&quot;code&quot;</span>, <span class="string">&quot;SUCCESS&quot;</span>);</span><br><span class="line">            res.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;该订单已成功关闭&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">String</span> <span class="variable">bodyAsString</span> <span class="operator">=</span> EntityUtils.toString(response.getEntity());</span><br><span class="line">        res = JSON.parseObject(bodyAsString,HashMap.class);</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (IOException | ParseException e) &#123;</span><br><span class="line">        res.put(<span class="string">&quot;code&quot;</span>, <span class="string">&quot;ERROR&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span> (e.toString() != <span class="literal">null</span> &amp;&amp; !e.toString().equals(<span class="string">&quot;&quot;</span>)) &#123;</span><br><span class="line">            res.put(<span class="string">&quot;message&quot;</span>, e.toString());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            res.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;发生未知错误&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>打印返回体，能得到具体的相关信息。API字典也做出了说明。</p>
<p>若成功，返回体为空，状态码为200或204。若失败，例如：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/b6064f58ad650b46a39b03d8add91b1f.png" alt="image-20220311161512533"></p>
<h3 id="查询订单"><a href="#查询订单" class="headerlink" title="查询订单"></a>查询订单</h3><p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml">查询订单API字典</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 可通过“微信支付订单号查询”和“商户订单号查询”两种方式查询订单详情</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * 这里通过后者进行查询</span></span><br><span class="line"><span class="comment"> * &lt;p&gt;</span></span><br><span class="line"><span class="comment"> * API字典： https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_2.shtml</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> orderNo</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@throws</span> Exception</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Override</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">queryOrder</span><span class="params">(String orderNo)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line"></span><br><span class="line">    log.info(<span class="string">&quot;查单接口调用 ===&gt; &#123;&#125;&quot;</span>, orderNo);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//拼接请求的第三方API</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">url</span> <span class="operator">=</span> String.format(WxApiType.ORDER_QUERY_BY_NO.getType(), orderNo);</span><br><span class="line">    url = wxPayConfig.getDomain().concat(url).concat(<span class="string">&quot;?mchid=&quot;</span>).concat(wxPayConfig.getMchId());</span><br><span class="line"></span><br><span class="line">    <span class="type">HttpGet</span> <span class="variable">httpGet</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">HttpGet</span>(url);</span><br><span class="line">    httpGet.setHeader(<span class="string">&quot;Accept&quot;</span>, <span class="string">&quot;application/json&quot;</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">//完成签名并执行请求</span></span><br><span class="line">    <span class="type">CloseableHttpResponse</span> <span class="variable">response</span> <span class="operator">=</span> httpClient.execute(httpGet);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">bodyAsString</span> <span class="operator">=</span> EntityUtils.toString(response.getEntity());<span class="comment">//响应体</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">statusCode</span> <span class="operator">=</span> response.getStatusLine().getStatusCode();<span class="comment">//响应状态码</span></span><br><span class="line">        <span class="keyword">if</span> (statusCode == <span class="number">200</span>) &#123; <span class="comment">//处理成功</span></span><br><span class="line">            log.info(<span class="string">&quot;成功, 返回结果 = &quot;</span> + bodyAsString);</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (statusCode == <span class="number">204</span>) &#123; <span class="comment">//处理成功，无返回Body</span></span><br><span class="line">            log.info(<span class="string">&quot;成功&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;查单接口调用,响应码 = &quot;</span> + statusCode + <span class="string">&quot;,返回结果 = &quot;</span> + bodyAsString);</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IOException</span>(<span class="string">&quot;request failed&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> bodyAsString;</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        response.close();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>API测试：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/4aca03dd79f4e8a2f8ba415797685bfe.png" alt="image-20220311164040114"></p>
<p>返回JSON：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">  <span class="attr">&quot;code&quot;</span><span class="punctuation">:</span> <span class="number">0</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;message&quot;</span><span class="punctuation">:</span> <span class="string">&quot;查询成功&quot;</span><span class="punctuation">,</span></span><br><span class="line">  <span class="attr">&quot;data&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">    <span class="attr">&quot;result&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">      <span class="attr">&quot;mchid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;162****542&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;out_trade_no&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ORDER_20220311155916957&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;trade_state&quot;</span><span class="punctuation">:</span> <span class="string">&quot;CLOSED&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;promotion_detail&quot;</span><span class="punctuation">:</span> <span class="punctuation">[</span><span class="punctuation">]</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;appid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;wx32d4*******79746&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;trade_state_desc&quot;</span><span class="punctuation">:</span> <span class="string">&quot;订单已关闭&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;attach&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">      <span class="attr">&quot;payer&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span><span class="punctuation">&#125;</span></span><br><span class="line">    <span class="punctuation">&#125;</span></span><br><span class="line">  <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="支付回调"><a href="#支付回调" class="headerlink" title="支付回调"></a>支付回调</h3><p>当用户下单后，微信服务器会请求我们的服务器，告知我们支付结果。但这并不安全，因为我们并不能确定请求服务器来自哪里，万一是黑客的恶意请求呢？于是微信强烈建议我们进行签名验证，确认受否微信支付服务器所请求且数据完整未被中途篡改</p>
<p><a target="_blank" rel="noopener" href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_5.shtml">支付回调API字典</a>详细解释了Request内容和对Resource解密后的内容，以及我们该如何回应微信服务器。如果微信收到商户的应答不符合规范或超时，微信认为通知失败，微信会通过一定的策略定期重新发起通知，尽可能提高通知的成功率，但微信不保证通知最终能成功。（通知频率为15s&#x2F;15s&#x2F;30s&#x2F;3m&#x2F;10m&#x2F;20m&#x2F;30m&#x2F;30m&#x2F;30m&#x2F;60m&#x2F;3h&#x2F;3h&#x2F;3h&#x2F;6h&#x2F;6h - 总计 24h4m）</p>
<blockquote>
<p> 在看懂SDK文档回调方案中我贴上了SDK提供的做法，这里我如法炮制</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 支付通知&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 微信支付通过支付通知接口将用户支付成功消息通知给商户&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 商户应返回应答&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 若商户收到的商户的应答不符合规范或者超时 微信则认为通知失败&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 若通知失败 微信会通过一定的策略定期重新发起通知&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 加密不能保证通知请求来自微信&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 微信会对发送给商户的通知进行签名&lt;br&gt;</span></span><br><span class="line"><span class="comment"> * 并将签名值放在通知的HTTP头Wechatpay-Signature&lt;br&gt;</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> response</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 响应map</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@ApiOperation(&quot;支付通知&quot;)</span></span><br><span class="line"><span class="meta">@PostMapping(&quot;/native/notify&quot;)</span></span><br><span class="line"><span class="keyword">public</span> String <span class="title function_">nativeNotify</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 应答对象</span></span><br><span class="line">    Map&lt;String, String&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 处理参数</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">serialNumber</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;Wechatpay-Serial&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">nonce</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;Wechatpay-Nonce&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">timestamp</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;Wechatpay-Timestamp&quot;</span>);</span><br><span class="line">        <span class="type">String</span> <span class="variable">signature</span> <span class="operator">=</span> request.getHeader(<span class="string">&quot;Wechatpay-Signature&quot;</span>);<span class="comment">// 请求头Wechatpay-Signature</span></span><br><span class="line">        <span class="comment">// 获取请求体</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">body</span> <span class="operator">=</span> HttpUtils.readData(request);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构造微信请求体</span></span><br><span class="line">        <span class="type">NotificationRequest</span> <span class="variable">wxRequest</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">NotificationRequest</span>.Builder().withSerialNumber(serialNumber)</span><br><span class="line">                .withNonce(nonce)</span><br><span class="line">                .withTimestamp(timestamp)</span><br><span class="line">                .withSignature(signature)</span><br><span class="line">                .withBody(body)</span><br><span class="line">                .build();</span><br><span class="line">        <span class="type">Notification</span> <span class="variable">notification</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">/**</span></span><br><span class="line"><span class="comment">             * 使用微信支付回调请求处理器解析构造的微信请求体</span></span><br><span class="line"><span class="comment">             * 在这个过程中会进行签名验证，并解密加密过的内容</span></span><br><span class="line"><span class="comment">             * 签名源码：  com.wechat.pay.contrib.apache.httpclient.cert; 271行开始</span></span><br><span class="line"><span class="comment">             * 解密源码：  com.wechat.pay.contrib.apache.httpclient.notification 76行</span></span><br><span class="line"><span class="comment">             *           com.wechat.pay.contrib.apache.httpclient.notification 147行 使用私钥获取AesUtil</span></span><br><span class="line"><span class="comment">             *           com.wechat.pay.contrib.apache.httpclient.notification 147行 使用Aes对称解密获得原文</span></span><br><span class="line"><span class="comment">             */</span></span><br><span class="line">            notification = notificationHandler.parse(wxRequest);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;通知验签失败&quot;</span>);</span><br><span class="line">            <span class="comment">//失败应答</span></span><br><span class="line">            response.setStatus(<span class="number">500</span>);</span><br><span class="line">            map.put(<span class="string">&quot;code&quot;</span>, <span class="string">&quot;ERROR&quot;</span>);</span><br><span class="line">            map.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;通知验签失败&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span> JSON.toJSONString(map);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 从notification中获取解密报文,并解析为HashMap</span></span><br><span class="line">        <span class="type">String</span> <span class="variable">plainText</span> <span class="operator">=</span> notification.getDecryptData();</span><br><span class="line">        log.info(<span class="string">&quot;通知验签成功&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//处理订单</span></span><br><span class="line">        wxPayService.processOrder(plainText);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//成功应答</span></span><br><span class="line">        response.setStatus(<span class="number">200</span>);</span><br><span class="line">        map.put(<span class="string">&quot;code&quot;</span>, <span class="string">&quot;SUCCESS&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;成功&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> JSON.toJSONString(map);</span><br><span class="line"></span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        <span class="comment">//失败应答</span></span><br><span class="line">        response.setStatus(<span class="number">500</span>);</span><br><span class="line">        map.put(<span class="string">&quot;code&quot;</span>, <span class="string">&quot;ERROR&quot;</span>);</span><br><span class="line">        map.put(<span class="string">&quot;message&quot;</span>, <span class="string">&quot;失败&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span> JSON.toJSONString(map);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>避坑：serialNumber参数值并不是我们在yml中所配置的，微信会重新发送一个新的证书序列号放在请求头，我们必须拼接这个证书序列号去换取证书实例，换取公钥验签</p>
</blockquote>
<p>调试是可以看到：</p>
<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/160c8c3a5e1440c79e8a2744f7ac9960.png" alt="image-20220311170652326"></p>
<p>HttpUtils是我用来读取HttpServletRequest中主体内容的工具类，源码如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HttpUtils</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 将通知参数转化为字符串</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> request</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> String <span class="title function_">readData</span><span class="params">(HttpServletRequest request)</span> &#123;</span><br><span class="line">        <span class="type">BufferedReader</span> <span class="variable">br</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">StringBuilder</span> <span class="variable">result</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StringBuilder</span>();</span><br><span class="line">            br = request.getReader();</span><br><span class="line">            <span class="keyword">for</span> (String line; (line = br.readLine()) != <span class="literal">null</span>; ) &#123;</span><br><span class="line">                <span class="keyword">if</span> (result.length() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                    result.append(<span class="string">&quot;\n&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                result.append(line);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> result.toString();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (br != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    br.close();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对于签名和解密是如何实现感到好奇的朋友可以到SDK中查看，相关源码位置我也写在注释中了</p>
<p>简单说明一下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">verify</span><span class="params">(String serialNumber, <span class="type">byte</span>[] message, String signature)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (!serialNumber.isEmpty() &amp;&amp; message.length != <span class="number">0</span> &amp;&amp; !signature.isEmpty()) &#123;</span><br><span class="line">                <span class="type">BigInteger</span> <span class="variable">serialNumber16Radix</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigInteger</span>(serialNumber, <span class="number">16</span>);</span><br><span class="line">                ConcurrentHashMap&lt;BigInteger, X509Certificate&gt; merchantCertificates = (ConcurrentHashMap)CertificatesManager.<span class="built_in">this</span>.certificates.get(<span class="built_in">this</span>.merchantId);</span><br><span class="line">                <span class="type">X509Certificate</span> <span class="variable">certificate</span> <span class="operator">=</span> (X509Certificate)merchantCertificates.get(serialNumber16Radix);</span><br><span class="line">                <span class="keyword">if</span> (certificate == <span class="literal">null</span>) &#123;</span><br><span class="line">                    CertificatesManager.log.error(<span class="string">&quot;商户证书为空，serialNumber:&#123;&#125;&quot;</span>, serialNumber);</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="type">Signature</span> <span class="variable">sign</span> <span class="operator">=</span> Signature.getInstance(<span class="string">&quot;SHA256withRSA&quot;</span>);</span><br><span class="line">                        sign.initVerify(certificate);</span><br><span class="line">                        sign.update(message);</span><br><span class="line">                        <span class="keyword">return</span> sign.verify(Base64.getDecoder().decode(signature));</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (NoSuchAlgorithmException var8) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;当前Java环境不支持SHA256withRSA&quot;</span>, var8);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (SignatureException var9) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;签名验证过程发生了错误&quot;</span>, var9);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InvalidKeyException var10) &#123;</span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;无效的证书&quot;</span>, var10);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;serialNumber或message或signature为空&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>在这里进行获取证书换取公钥签名</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setDecryptData</span><span class="params">(Notification notification)</span> <span class="keyword">throws</span> ParseException &#123;</span><br><span class="line">        <span class="type">Resource</span> <span class="variable">resource</span> <span class="operator">=</span> notification.getResource();</span><br><span class="line">        <span class="type">String</span> <span class="variable">getAssociateddData</span> <span class="operator">=</span> <span class="string">&quot;&quot;</span>;</span><br><span class="line">        <span class="keyword">if</span> (resource.getAssociatedData() != <span class="literal">null</span>) &#123;</span><br><span class="line">            getAssociateddData = resource.getAssociatedData();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="type">byte</span>[] associatedData = getAssociateddData.getBytes(StandardCharsets.UTF_8);</span><br><span class="line">        <span class="type">byte</span>[] nonce = resource.getNonce().getBytes(StandardCharsets.UTF_8);</span><br><span class="line">        <span class="type">String</span> <span class="variable">ciphertext</span> <span class="operator">=</span> resource.getCiphertext();</span><br><span class="line">        <span class="type">AesUtil</span> <span class="variable">aesUtil</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AesUtil</span>(<span class="built_in">this</span>.apiV3Key);</span><br><span class="line"></span><br><span class="line">        String decryptData;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            decryptData = aesUtil.decryptToString(associatedData, nonce, ciphertext);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (GeneralSecurityException var10) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">ParseException</span>(<span class="string">&quot;AES解密失败，resource：&quot;</span> + resource.toString(), var10);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        notification.setDecryptData(decryptData);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>凭借私钥获取AES口令，解密ciphertext中的内容</p>
</blockquote>
<p>实测一下：</p>
<img src="https://0-bit.oss-cn-beijing.aliyuncs.com/tsQQ图片20220311170526.jpg"  />

<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/fe570790b68cbd3c62facd35030c8120.png" alt="image-20220311170923166"></p>
<p>resource内容是被加密过的</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">2022-03-11 17:11:23.379  INFO 1656 --- [nio-8080-exec-1] t.m.m.w.service.impl.WxPayServiceImpl    : 生成订单</span><br><span class="line">2022-03-11 17:11:23.479  INFO 1656 --- [nio-8080-exec-1] t.m.m.w.service.impl.WxPayServiceImpl    : 调用统一下单API</span><br><span class="line">2022-03-11 17:11:23.523  INFO 1656 --- [nio-8080-exec-1] t.m.m.w.service.impl.WxPayServiceImpl    : 请求参数 ===&gt; &#123;&#125;&#123;&quot;amount&quot;:&#123;&quot;total&quot;:1,&quot;currency&quot;:&quot;CNY&quot;&#125;,&quot;mchid&quot;:&quot;1621810542&quot;,&quot;out_trade_no&quot;:&quot;ORDER_20220311171123529&quot;,&quot;appid&quot;:&quot;wx32d4d97357b79746&quot;,&quot;description&quot;:&quot;GBA游戏测评&quot;,&quot;notify_url&quot;:&quot;http://maiqu.sh1.k9s.run:2271/api/wx-pay/native/notify&quot;&#125;</span><br><span class="line">2022-03-11 17:11:23.926  INFO 1656 --- [nio-8080-exec-1] t.m.m.w.service.impl.WxPayServiceImpl    : 成功, 返回结果 = &#123;&quot;code_url&quot;:&quot;weixin://wxpay/bizpayurl?pr=ICd695Azz&quot;&#125;</span><br><span class="line">2022-03-11 17:11:31.783 ERROR 1656 --- [nio-8080-exec-2] t.m.m.s.f.JWTAuthenticationTokenFilter   : Token为空</span><br><span class="line">2022-03-11 17:19:53.337  WARN 1656 --- [l-1 housekeeper] com.zaxxer.hikari.pool.HikariPool        : HikariPool-1 - Thread starvation or clock leap detected (housekeeper delta=8m29s873ms509µs200ns).</span><br><span class="line">2022-03-11 17:19:53.338  INFO 1656 --- [nio-8080-exec-2] t.maiquer.metrichall.wxpay.api.WxPayAPI  : 通知验签成功</span><br></pre></td></tr></table></figure>

<p>解密内容：</p>
<figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line">	<span class="attr">&quot;mchid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;1621***42&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;appid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;wx32d*****79746&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;out_trade_no&quot;</span><span class="punctuation">:</span> <span class="string">&quot;ORDER_20220311171123529&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;transaction_id&quot;</span><span class="punctuation">:</span> <span class="string">&quot;4200001348202203119819934409&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;trade_type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;NATIVE&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;trade_state&quot;</span><span class="punctuation">:</span> <span class="string">&quot;SUCCESS&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;trade_state_desc&quot;</span><span class="punctuation">:</span> <span class="string">&quot;支付成功&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;bank_type&quot;</span><span class="punctuation">:</span> <span class="string">&quot;OTHERS&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;attach&quot;</span><span class="punctuation">:</span> <span class="string">&quot;&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;success_time&quot;</span><span class="punctuation">:</span> <span class="string">&quot;2022-03-11T17:11:31+08:00&quot;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;payer&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">		<span class="attr">&quot;openid&quot;</span><span class="punctuation">:</span> <span class="string">&quot;o0F3X099H******Spqj5p8D-6TI&quot;</span></span><br><span class="line">	<span class="punctuation">&#125;</span><span class="punctuation">,</span></span><br><span class="line">	<span class="attr">&quot;amount&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line">		<span class="attr">&quot;total&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">		<span class="attr">&quot;payer_total&quot;</span><span class="punctuation">:</span> <span class="number">1</span><span class="punctuation">,</span></span><br><span class="line">		<span class="attr">&quot;currency&quot;</span><span class="punctuation">:</span> <span class="string">&quot;CNY&quot;</span><span class="punctuation">,</span></span><br><span class="line">		<span class="attr">&quot;payer_currency&quot;</span><span class="punctuation">:</span> <span class="string">&quot;CNY&quot;</span></span><br><span class="line">	<span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>

<p><img src="https://0-bit.oss-cn-beijing.aliyuncs.com/1c7c9eb848adc654a680e62f5f964977.png"></p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>写到这里就告一段落了</p>
<p>相关的数据库表和实体类我没有提供，各位根据业务个性化设计，至于怎么使用微信支付SDK本文已交代的很清楚</p>
<p>后面还有退款、订单超时、下载账单等API。怎么使用都大差不差，无非组装请求体，使用SDK提供的HttpClient请求，省略繁琐的安全验证过程，得到返回结果…大家自己摸索，多说无益</p>
<p>需要完整实例源码的可私信我</p>

  </div>
</article>


    <div class="blog-post-comments">
        <div id="utterances_thread">
            <noscript>Please enable JavaScript to view the comments.</noscript>
        </div>
    </div>


        
          <div id="footer-post-container">
  <div id="footer-post">

    <div id="nav-footer" style="display: none">
      <ul>
         
          <li><a href="/">Home</a></li>
         
          <li><a href="/about/">About</a></li>
         
          <li><a href="/archives/">Articles</a></li>
         
          <li><a href="/categories/">Category</a></li>
         
          <li><a href="/search/">Search</a></li>
        
      </ul>
    </div>

    <div id="toc-footer" style="display: none">
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%86%99%E5%9C%A8%E5%89%8D%E9%9D%A2"><span class="toc-number">1.</span> <span class="toc-text">写在前面</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%86%9F%E6%82%89%E5%AE%98%E6%96%B9%E6%96%87%E6%A1%A3"><span class="toc-number">2.</span> <span class="toc-text">熟悉官方文档</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E7%9C%8B%E6%87%82SDK%E6%96%87%E6%A1%A3"><span class="toc-number">3.</span> <span class="toc-text">看懂SDK文档</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%81%9A%E5%A5%BD%E5%87%86%E5%A4%87"><span class="toc-number">3.1.</span> <span class="toc-text">做好准备</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%80%E5%A7%8B"><span class="toc-number">3.2.</span> <span class="toc-text">开始</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A1%AB%E5%9D%91"><span class="toc-number">3.3.</span> <span class="toc-text">填坑</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%9B%9E%E8%B0%83%E6%96%B9%E6%A1%88"><span class="toc-number">3.4.</span> <span class="toc-text">回调方案</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%BC%80%E5%A7%8B%E5%B9%B2%E6%B4%BB"><span class="toc-number">4.</span> <span class="toc-text">开始干活</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%86%8D%E5%87%86%E5%A4%87%E4%B8%80%E6%AC%A1"><span class="toc-number">4.1.</span> <span class="toc-text">再准备一次</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B3%A8%E5%86%8C%E5%85%A8%E5%B1%80Bean-%E4%BE%9B%E4%B8%9A%E5%8A%A1%E4%BD%BF%E7%94%A8"><span class="toc-number">4.2.</span> <span class="toc-text">注册全局Bean 供业务使用</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%B1%E4%BA%8E%E5%A4%9A%E5%A4%84%E9%9C%80%E8%A6%81%E5%8A%A0%E8%BD%BD%E7%A7%81%E9%92%A5%EF%BC%8C%E4%BE%BF%E6%B3%A8%E5%86%8C%E4%B8%80%E4%B8%AA%E8%BF%94%E5%9B%9E%E7%A7%81%E9%92%A5%E5%86%85%E5%AE%B9%E7%9A%84Bean"><span class="toc-number">4.2.1.</span> <span class="toc-text">由于多处需要加载私钥，便注册一个返回私钥内容的Bean</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E9%AA%8C%E7%AD%BE%E5%99%A8"><span class="toc-number">4.2.2.</span> <span class="toc-text">获取验签器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96HttpClient"><span class="toc-number">4.2.3.</span> <span class="toc-text">获取HttpClient</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%8E%B7%E5%8F%96%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E5%99%A8"><span class="toc-number">4.2.4.</span> <span class="toc-text">获取支付回调请求处理器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%90%AF%E5%8A%A8%E6%9C%8D%E5%8A%A1%E5%81%9A%E6%B5%8B%E8%AF%95"><span class="toc-number">4.2.5.</span> <span class="toc-text">启动服务做测试</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E9%9B%86%E4%B8%AD%E5%B0%81%E8%A3%85%E4%B8%80%E4%BA%9B%E6%9E%9A%E4%B8%BE%E7%B1%BB"><span class="toc-number">4.3.</span> <span class="toc-text">集中封装一些枚举类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B0%81%E8%A3%85%E5%93%8D%E5%BA%94%E6%B6%88%E6%81%AF"><span class="toc-number">4.4.</span> <span class="toc-text">封装响应消息</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Native%E4%B8%8B%E5%8D%95"><span class="toc-number">4.5.</span> <span class="toc-text">Native下单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%96%E6%B6%88%E8%AE%A2%E5%8D%95"><span class="toc-number">4.6.</span> <span class="toc-text">取消订单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E8%AF%A2%E8%AE%A2%E5%8D%95"><span class="toc-number">4.7.</span> <span class="toc-text">查询订单</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%94%AF%E4%BB%98%E5%9B%9E%E8%B0%83"><span class="toc-number">4.8.</span> <span class="toc-text">支付回调</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%80%BB%E7%BB%93"><span class="toc-number">5.</span> <span class="toc-text">总结</span></a></li></ol>
    </div>

    <div id="share-footer" style="display: none">
      <ul>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.facebook.com/sharer.php?u=https://cheung0-bit.github.io/7f59debf3aa3/"><i class="fab fa-facebook fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://twitter.com/share?url=https://cheung0-bit.github.io/7f59debf3aa3/&text=跟我一起玩转微信APIv3支付"><i class="fab fa-twitter fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.linkedin.com/shareArticle?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-linkedin fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://pinterest.com/pin/create/bookmarklet/?url=https://cheung0-bit.github.io/7f59debf3aa3/&is_video=false&description=跟我一起玩转微信APIv3支付"><i class="fab fa-pinterest fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" href="mailto:?subject=跟我一起玩转微信APIv3支付&body=Check out this article: https://cheung0-bit.github.io/7f59debf3aa3/"><i class="fas fa-envelope fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://getpocket.com/save?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-get-pocket fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://reddit.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-reddit fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.stumbleupon.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-stumbleupon fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://digg.com/submit?url=https://cheung0-bit.github.io/7f59debf3aa3/&title=跟我一起玩转微信APIv3支付"><i class="fab fa-digg fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="http://www.tumblr.com/share/link?url=https://cheung0-bit.github.io/7f59debf3aa3/&name=跟我一起玩转微信APIv3支付&description="><i class="fab fa-tumblr fa-lg" aria-hidden="true"></i></a></li>
  <li><a class="icon" target="_blank" rel="noopener" href="https://news.ycombinator.com/submitlink?u=https://cheung0-bit.github.io/7f59debf3aa3/&t=跟我一起玩转微信APIv3支付"><i class="fab fa-hacker-news fa-lg" aria-hidden="true"></i></a></li>
</ul>

    </div>

    <div id="actions-footer">
        <a id="menu" class="icon" href="#" onclick="$('#nav-footer').toggle();return false;"><i class="fas fa-bars fa-lg" aria-hidden="true"></i> Menu</a>
        <a id="toc" class="icon" href="#" onclick="$('#toc-footer').toggle();return false;"><i class="fas fa-list fa-lg" aria-hidden="true"></i> TOC</a>
        <a id="share" class="icon" href="#" onclick="$('#share-footer').toggle();return false;"><i class="fas fa-share-alt fa-lg" aria-hidden="true"></i> Share</a>
        <a id="top" style="display:none" class="icon" href="#" onclick="$('html, body').animate({ scrollTop: 0 }, 'fast');"><i class="fas fa-chevron-up fa-lg" aria-hidden="true"></i> Top</a>
    </div>

  </div>
</div>

        
        <footer id="footer">
  <div class="footer-left">
    Copyright &copy;
      
        
          2022-2024
            <a target="_blank" rel="noopener" href="https://beian.miit.gov.cn/">
              苏ICP备2022044873号
            </a>
  </div>
  <div class="footer-right">
    <nav>
      <ul>
        
          <!--
       -->
          <li><a href="/">
              Home
            </a></li>
          <!--
     -->
          
          <!--
       -->
          <li><a href="/about/">
              About
            </a></li>
          <!--
     -->
          
          <!--
       -->
          <li><a href="/archives/">
              Articles
            </a></li>
          <!--
     -->
          
          <!--
       -->
          <li><a href="/categories/">
              Category
            </a></li>
          <!--
     -->
          
          <!--
       -->
          <li><a href="/search/">
              Search
            </a></li>
          <!--
     -->
          
      </ul>
    </nav>
  </div>
</footer>
    </div>
    <!-- styles -->


 
  <link
    rel="preload"
    href="/lib/font-awesome/css/all.min.css"
    as="style"
    onload="this.onload=null;this.rel='stylesheet'"
  />
  <noscript
    ><link
      rel="stylesheet"
      href="/lib/font-awesome/css/all.min.css"
  /></noscript>



    <!-- jquery -->
 
  
<script src="/lib/jquery/jquery.min.js"></script>





<!-- clipboard -->

   
    
<script src="/lib/clipboard/clipboard.min.js"></script>

  
  <script type="text/javascript">
  $(function() {
    // copy-btn HTML
    var btn = "<span class=\"btn-copy tooltipped tooltipped-sw\" aria-label=\"Copy to clipboard!\">";
    btn += '<i class="far fa-clone"></i>';
    btn += '</span>'; 
    // mount it!
    $(".highlight table").before(btn);
    var clip = new ClipboardJS('.btn-copy', {
      text: function(trigger) {
        return Array.from(trigger.nextElementSibling.querySelectorAll('.code')).reduce((str,it)=>str+it.innerText+'\n','')
      }
    });
    clip.on('success', function(e) {
      e.trigger.setAttribute('aria-label', "Copied!");
      e.clearSelection();
    })
  })
  </script>


<script src="/js/main.js"></script>

<!-- search -->

<!-- Google Analytics -->

<!-- Baidu Analytics -->

  <script type="text/javascript">
        var _hmt = _hmt || [];
        (function() {
          var hm = document.createElement("script");
          hm.src = "https://hm.baidu.com/hm.js?4484a4a33014b59670c470a6e15a66db";
          var s = document.getElementsByTagName("script")[0];
          s.parentNode.insertBefore(hm, s);
        })();
        </script>

<!-- Cloudflare Analytics -->

<!-- Umami Analytics -->

<!-- Disqus Comments -->

<!-- utterances Comments -->

    <script type="text/javascript">
      var utterances_repo = 'Cheung0-bit/blog-package';
      var utterances_issue_term = 'title';
      var utterances_label = 'Comment';
      var utterances_theme = 'github-dark';

      (function(){
          var script = document.createElement('script');

          script.src = 'https://utteranc.es/client.js';
          script.setAttribute('repo', utterances_repo);
          script.setAttribute('issue-term', 'pathname');
          script.setAttribute('label', utterances_label);
          script.setAttribute('theme', utterances_theme);
          script.setAttribute('crossorigin', 'anonymous');
          script.async = true;
          (document.getElementById('utterances_thread')).appendChild(script);
      }());
  </script>

</body>
</html>
